{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "religious-curve",
   "metadata": {},
   "source": [
    "# Consumption and Generation Reference System"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "furnished-anger",
   "metadata": {},
   "source": [
    "This tutorial is about the effect of the sign of active and reactive power in load and sgen in pandapower. In this tutorial, the load element is used to model power consumption and the static generator element is used to model power generation. In pandapower, the convention for consumption and generation is different. This difference between consumer frame convention and generator frame convention will be demonstrated here."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "lovely-manhattan",
   "metadata": {},
   "source": [
    "![reference_system](pics/reference_system.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "considerable-holiday",
   "metadata": {},
   "source": [
    "![](l.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "working-thumb",
   "metadata": {},
   "source": [
    "In regard to reactive power, there are 2 states for the 4 quadrants of each frame convention: under-excited and over-excited (see figure above).\n",
    "\n",
    "In the Consumer Frame Convention, which is used for load-like elements (load, shunt, ward, xward, storage), the term ‘under-excited’ is used to indicate that the load is in I and II quadrants and is absorbing reactive power (decreases voltage) and the term ‘over-excited’ is used to indicate that the load is in the III and IV quadrants and is injecting reactive power (increases voltage).\n",
    "\n",
    "In the Generator Frame Convention, which is used for generator-like elements (gen, sgen, ext_grid), the term ‘over-excited’ is used to indicate the generator is in the I and II quadrants and is injecting reactive power (increases voltage), and the term ‘under-excited’ is used to indicate the generator is in III and IV quadrants and is absorbing reactive power (decreases voltage)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "immune-evans",
   "metadata": {},
   "source": [
    "We show the differences between the reference systems with the following example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "understood-fellowship",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T12:14:26.930943Z",
     "start_time": "2025-10-20T12:14:17.155139Z"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "from pandapower.create import (\n",
    "    create_empty_network,\n",
    "    create_bus,\n",
    "    create_line,\n",
    "    create_load,\n",
    "    create_ext_grid,\n",
    "    create_sgen\n",
    ")\n",
    "from pandapower.run import runpp\n",
    "import pandas as pd\n",
    "import seaborn as sn"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fatty-proposal",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T12:14:28.248721Z",
     "start_time": "2025-10-20T12:14:27.142138Z"
    }
   },
   "outputs": [],
   "source": [
    "net=create_empty_network()\n",
    "bus0 = create_bus(net, name=\"Bus 0\", vn_kv=110, type=\"b\")\n",
    "bus1 = create_bus(net, name=\"Bus 1\", vn_kv=110, type=\"b\")\n",
    "line0 = create_line(net, bus0, bus1, length_km=50, std_type=\"70-AL1/11-ST1A 110.0\",  name=\"Line 1\")\n",
    "create_sgen(net, bus1, p_mw=0, q_mvar=0, name=\"static generator\")\n",
    "create_ext_grid(net, bus0, vm_pu=1.02, va_degree=50)\n",
    "create_load(net, bus1, p_mw=0, q_mvar=0, name=\"load i\")\n",
    "net"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "honey-serbia",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T12:14:28.450585Z",
     "start_time": "2025-10-20T12:14:28.422143Z"
    }
   },
   "outputs": [],
   "source": [
    "df=pd.DataFrame(columns=['element', 'p_mw', 'q_mvar', 'vm_pu', 'p_from_mw', 'p_to_mw'])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "suspended-coffee",
   "metadata": {},
   "source": [
    "We will take three variations of p_mw (0, 50, -50) and three variations of q_mvar (0, 25, -25) for both load and sgen to analyze the output of the voltages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "weird-argument",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T12:14:38.546042Z",
     "start_time": "2025-10-20T12:14:28.831884Z"
    }
   },
   "outputs": [],
   "source": [
    "i=0\n",
    "for element in ('load', 'sgen'):\n",
    "    for x in [0, 50, -50]:\n",
    "        for y in [0, 25, -25]:\n",
    "            net[element].loc[0,['p_mw', 'q_mvar']] = x, y\n",
    "            runpp(net)\n",
    "            df.loc[i, 'element'] = element\n",
    "            df.loc[i, ['p_mw', 'q_mvar']] = x,y\n",
    "            df.loc[i, 'vm_pu'] = net.res_bus.vm_pu.at[bus1]\n",
    "            df.loc[i, 'p_from_mw'] = net.res_line.p_from_mw.at[line0]\n",
    "            df.loc[i, 'p_to_mw'] = net.res_line.p_to_mw.at[line0]\n",
    "            i += 1\n",
    "    net[element].loc[0, ['p_mw', 'q_mvar']] = 0, 0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "acceptable-appointment",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T12:14:38.701634Z",
     "start_time": "2025-10-20T12:14:38.630903Z"
    }
   },
   "outputs": [],
   "source": [
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "empty-thursday",
   "metadata": {},
   "source": [
    "If you look at the 4th index, you will see both the active and reactive power of the load are positive and the voltage has been reduced. This is a result of consumption of P and Q in the under-excited state. In the 5th index the active power is positive but the reactive power is negative and as the result, the voltage does not drop as much as in the case of index 4."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "scheduled-gibson",
   "metadata": {},
   "source": [
    "Now we will see the plots for both the elements where the reference systems can be compared and the vm_pu values are shown for different combinations of active and reactive power."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ruled-specific",
   "metadata": {},
   "source": [
    "Notice that, in the plot for the load element, when the sign of reactive power is negative, the reactive power is injected, which increases voltage. And when the reactive power is positive, the voltage drops because the reactive power is absorbed. The opposite effect can be seen for sgen elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "potential-worker",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-20T12:14:44.697651Z",
     "start_time": "2025-10-20T12:14:39.988685Z"
    }
   },
   "outputs": [],
   "source": [
    "import matplotlib.patches as mpatches\n",
    "import matplotlib.pyplot as plt\n",
    "g=sn.relplot(x=\"p_mw\", \n",
    "        y=\"q_mvar\",\n",
    "        size=\"vm_pu\",\n",
    "        col=\"element\",\n",
    "        sizes=(500,500),\n",
    "        palette=\"viridis\",\n",
    "        hue=\"vm_pu\",\n",
    "        data=df, alpha=1, zorder=10)\n",
    "g.set(xlim=(-60,60),ylim=(-60,60))\n",
    "\n",
    "texts =[['Under-excited (consumes reactive power, decreases voltage)', 'Over-excited (injects reactive power, increases voltage)'],\n",
    "        ['Over-excited (injects reactive power, increases voltage)', 'Under-excited (consumes reactive power, decreases voltage)']]\n",
    "        \n",
    "for i,(ax, text) in enumerate(zip(g.axes[0], texts)):\n",
    "    # Move left y-axis and bottom x-axis to centre, passing through (0,0)\n",
    "    ax.spines['left'].set_position('center')\n",
    "    ax.spines['bottom'].set_position('center')\n",
    "    \n",
    "    ax.xaxis.labelpad = 160\n",
    "    ax.yaxis.labelpad = 160\n",
    "    ax.grid()\n",
    "    ax.text(0,50, text[0], bbox={'facecolor': 'lightgrey', 'pad': 10}, horizontalalignment='center', zorder=10)\n",
    "    ax.text(0,-50, text[1], bbox={'facecolor': 'lightgrey', 'pad': 10}, horizontalalignment='center', zorder=10)\n",
    "    \n",
    "    rect=mpatches.Rectangle((0,-25),50,50, alpha=0.1,facecolor=\"blue\")\n",
    "    ax.add_patch(rect)\n",
    "    \n",
    "\n",
    "    # add text with text() function in matplotlib\n",
    "    ax.text(10, 10,['P consumption','P generation'][i],fontsize=12)\n",
    "\n",
    "\n",
    "   \n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
